package com.wei.czz.framework.common.handler.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-08-11 16:48:00
 * className: RedisHashHelper Redis-Hash数据结构操作工具类
 * version: 1.0
 * description:
 */
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisHashHandler {

    /*
        注意：
          1、所有向Hash数据结构添加数据操作，值类型统一采用‘泛型’设计，且同一个Hash数据结构存储的‘所有值’最好保持‘类型统一’，需‘开发者’确保所有‘值’的类型统一
            1.1、由于Redis的key-value特性，所有‘添加值’的跟随请求发送至Redis前都将进行‘序列化’操作，则在Redis不会再做类型校验，统一以‘字符串’的方式存储，
                 如果不能保证同一个Hash结构存储的所有值类型统一，则程序‘获取值’时进行‘反序列化’操作时，可能会引发‘类型转换异常’，产生重大事故。
            1.2、如果同一个Hash结构存储的值类型不一致，管理上也会增加额外的难度
     */

    private static final Logger log = LoggerFactory.getLogger(RedisHashHandler.class);

    /**
     * Redis-Hash数据结构操作模板对象
     */
    private final HashOperations hashOperations;

    @Autowired
    public RedisHashHandler(RedisTemplate redisTemplate) {
        this.hashOperations = redisTemplate.opsForHash();
    }

    /**
     * 操作key数据，向其添加一对hashKey-value键值对
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @param value   存储的值
     * @param <T>     泛型类型
     */
    public <T> void put(final String key, final String hashKey, final T value) {
        log.info("Hash类型 put操作 key={} hashKey={} value={}", key, hashKey, value);
        hashOperations.put(key, hashKey, value);
    }

    /**
     * 操作key数据，向其添加多对hashKey-value键值对
     * @param key Hash空间键
     * @param map 存储hashKey-value键值对的Map对象
     * @param <T> 泛型类型
     */
    public <T> void putAll(final String key, final Map<String, T> map) {
        log.info("Hash类型 putAll操作 key={} keys={} values={}", key, map.keySet(), map.values());
        hashOperations.putAll(key, map);
    }

    /**
     * 操作key数据，向其添加一对hashKey-value键值对，当hashKey不存在时，才进行添加操作，否则不做操作
     *
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @param value   存储的值
     * @param <T>     泛型类型
     * @return 添加成功返回true，不做操作返回false
     */
    public <T> Boolean putIfAbsent(final String key, final String hashKey, T value) {
        log.info("Hash类型 putIfAbsent操作 key={} hashKey={} value={}", key, hashKey, value);
        return hashOperations.putIfAbsent(key, hashKey, value);
    }

    /**
     * 操作key数据，根据hashKey获取其对应的值
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @param <T>     泛型类型
     * @return hashKey对应的值
     */
    public <T> T get(final String key, final String hashKey) {
        log.info("Hash类型 get操作 key={} hashKey={}", key, hashKey);
        return (T) hashOperations.get(key, hashKey);
    }

    /**
     * 操作key数据，根据hashKeys批量查询对应的value值
     * @param key      Hash空间键
     * @param hashKeys Hash子结构key
     * @param <T>      泛型类型
     * @return hashKeys对应的value值
     */
    public <T> List<T> multiGet(final String key, final String... hashKeys) {
        log.info("Hash类型 multiGet操作 key={} hashKeys={}", key, hashKeys);
        return hashOperations.multiGet(key, Arrays.asList(hashKeys));
    }

    /**
     * 操作key数据，根据hashKeys批量查询对应的value值
     * @param <T>      泛型类型
     * @param key      Hash空间键
     * @param hashKeys Hash子结构key
     * @return hashKeys对应的value值
     */
    public <T> List<T> multiGet(final String key, final Collection<String> hashKeys) {
        log.info("Hash类型 multiGet操作 key={} hashKeys={}", key, hashKeys);
        return hashOperations.multiGet(key, hashKeys);
    }

    /**
     * 操作key数据，获取key所有的hashKey-value键值对
     * @param key Hash空间键
     * @param <T> 泛型类型
     * @return key包含的所有hashKey-value键值对
     */
    public <T> Map<String, T> entries(final String key) {
        log.info("Hash类型 entries操作 key={}", key);
        return hashOperations.entries(key);
    }

    /**
     * 操作key数据，获取key包含的所有hashKey
     * @param key Hash空间键
     * @return key包含的所有hashKey
     */
    public Set<String> keys(final String key) {
        log.info("Hash类型 keys操作 key={}", key);
        return hashOperations.keys(key);
    }

    /**
     * 操作key数据，获取key包含的所有value
     * @param key Hash空间键
     * @return key包含的所有value
     */
    public <T> List<T> values(final String key) {
        log.info("Hash类型 values操作 key={}", key);
        return hashOperations.values(key);
    }

    /**
     * 操作key数据，key结构存储的所有键是否包含hashKey
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @return 包含返回true，不包含返回false
     */
    public Boolean hasKey(final String key, final String hashKey) {
        log.info("Hash类型 hasKey操作 key={} hashKey={}", key, hashKey);
        return hashOperations.hasKey(key, hashKey);
    }

    /**
     * 操作key数据，获取key包含的hashKey-value键值对个数
     * @param key Hash空间键
     * @return key包含的hashKey-value键值对个数
     */
    public Long size(final String key) {
        log.info("Hash类型 size操作 key={}", key);
        return hashOperations.size(key);
    }

    /**
     * 操作key数据，获取hashKey对应value值的长度
     * 注意：
     *  1、如果hashKey在key中不存在，则返回0
     *  2、如果value的数据类型为数字，则返回数字的长度,而不是数字的值
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @return value值的长度
     */
    public Long lengthOfValue(final String key, final String hashKey) {
        log.info("Hash类型 lengthOfValue操作 key={} hashKey={}", key, hashKey);
        return hashOperations.lengthOfValue(key, hashKey);
    }

    /**
     * 操作key数据，使用hashKey对应的value值加上num
     * 注意：
     *  1、hashKey对应的value值数据类型必须时数字类型，才能使用increment操作，否则报异常
     *  2、Hash结构没有decrement操作，如果要减去一个数字，则传入一个负数即可
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @param num     数字值
     * @return value加上num后的最新值
     */
    public Long increment(final String key, final String hashKey, final long num) {
        log.info("Hash类型 increment操作 key={} hashKey={} num={}", key, hashKey, num);
        return hashOperations.increment(key, hashKey, num);
    }

    /**
     * 操作key数据，使用hashKey对应的value值加上num
     * 注意：
     *  1、hashKey对应的value值数据类型必须时数字类型，才能使用increment操作，否则报异常
     *  2、Hash结构没有decrement操作，如果要减去一个数字，则传入一个负数即可
     *  3、当前操作可能会导致value值的精度丢失
     * @param key     Hash空间键
     * @param hashKey Hash子结构key
     * @param num     数字值
     * @return value加上num后的最新值
     */
    public Double increment(final String key, final String hashKey, final double num) {
        log.info("Hash类型 increment操作 key={} hashKey={} num={}", key, hashKey, num);
        return hashOperations.increment(key, hashKey, num);
    }

    /**
     * 操作key数据，从key结构中删除hashKey-value键值对
     * 注意：
     *  1、如果key不没有hashKey对应的键值对，则没有删除操作，返回0
     * @param key     Hash空间键
     * @param hashKey 一个或者多个Hash子结构key
     * @return 成功从key中删除hashKey-value键值对的个数
     */
    public Long delete(final String key, final String... hashKey) {
        log.info("Hash类型 delete操作 key={} hashKey={}", key, hashKey);
        return hashOperations.delete(key, hashKey);
    }

    /**
     * 操作key数据，从key结构中删除hashKey-value键值对
     * 注意：
     *  1、如果key不没有hashKey对应的键值对，则没有删除操作，返回0
     * @param key         Hash空间键
     * @param hashKeyList Hash子结构key列表
     * @return 成功从key中删除hashKey-value键值对的个数
     */
    public Long delete(final String key, final List<String> hashKeyList) {
        log.debug("Hash类型 delete操作 key={} hashKeyList={}", key, hashKeyList);
        if (CollectionUtils.isEmpty(hashKeyList)) {
            return 0L;
        }
        String[] hashKeys = hashKeyList.toArray(new String[0]);
        return hashOperations.delete(key, hashKeys);
    }



}
